import java.util.ArrayList;
import java.util.List;


public class State {

	byte[][] grid;
	byte[] fillArray;
	byte currentPlayer; // white: 1 & red: 2
	byte isRole; // Agent role. 
	int col, row;
	int[] move = new int[2];
	
	int adjWhite = 0;
	int adjRed = 0;
	
	boolean gameOver;
	String terminalType;
	byte gameWinner = 0;
	
	public State(String isRole)
	{
		col = 7;
		row = 6;
		currentPlayer = 1;
		gameOver = false;
		
		this.isRole = (byte)((isRole.equals("WHITE")) ? 1 : 2);
		
		grid = new byte[col][row];
		for (int x = 0; x < 7; ++x)
		{
			for (int y = 0; y < 6; ++y)
			{
				grid[x][y] = 0;
			}
		}
		
		fillArray = new byte[] {0,0,0,0,0,0,0};
		
	}
	
	// Copy constructor
	public State(State prev, int action)
	{
		this.col = prev.col;
		this.row = prev.row;
		this.adjWhite = prev.adjWhite;
		this.adjRed = prev.adjRed;
		this.grid = new byte[col][row];
		this.fillArray = new byte[col];
		
		System.arraycopy(prev.fillArray, 0, this.fillArray, 0, 6);
		copyGrid(prev.grid);
		
		this.currentPlayer = prev.currentPlayer;
		this.isRole = prev.isRole;
		
		this.makeMove(action);
		this.gameOver = prev.gameOver;
	}
	
	public void copyGrid(byte[][] oldGrid)
	{
		for (int i = 0; i < col; ++i)
		{
			System.arraycopy(oldGrid[i], 0, this.grid[i], 0, row);
		}
	}
	
	public State getSuccessor(int action)
	{
		return new State(this, action);
	}
	
	public void makeMove(int c)
	{
		grid[c][fillArray[c]] = currentPlayer;
		move = new int[] {c, fillArray[c]};
		fillArray[c]++;
		
		updateAdjacentNo();

		currentPlayer = (byte)((currentPlayer == 1) ? 2 : 1);

	}
	

	
	public ArrayList<Integer> legalMoves()
	{
		ArrayList<Integer> moves = new ArrayList<Integer>();
		for (int c = 0; c < col; ++c)
		{
			if (grid[c][row-1] == 0)
			{
				moves.add(c);
			}
		}
		return moves;
	}
	
	public boolean terminalTest()
	{
		if (checkWin() == true)
		{
			gameWinner = (byte)((currentPlayer == 1) ? 2 : 1);
			terminalType = "Win" + gameWinner;
			return true;
		}
		
		// Check for draw, i.e. the grid has been filled.
		if (checkDraw() == true)
		{
			terminalType = "draw";
			return true;
		}
		return false;
 		
	}
	
	public int evaluate(int depthLimit)
	{
		int eval = 0;
		if (checkWin() && terminalType.equals("Win" + gameWinner))
		{
			if (this.isRole == gameWinner)
			{
				if (depthLimit == 1)
				{
					System.out.println("lol");
				}
				eval += 1000/depthLimit;
				eval += 100;
			}
			else
			{
				eval -= 1000/depthLimit;
				eval -= 100;
			}
		}

		
		
		if (isRole == 1)
		{
			eval += (adjWhite - adjRed);
		}
		else
		{
			eval += (adjRed - adjWhite);
		}


		eval += getMoveScore(move[0]);
		
		return eval;
		
	}
	private int getMoveScore(int move)
	{
		int slotX = move;
		int slotY = getNextFreeSlot(move);
		
		int score = 0;
		
		int[] stepXArr = {0, 1, 1, 1};
		int[] stepYArr = {1, 1, 0,-1};
		for(int i = 0; i < 4; i++)
		{
			int stepX = stepXArr[i];
			int stepY = stepYArr[i];
			
			for(int j = 0; j < 4; j++)
			{
				int opponentRow = 0;
				int x = slotX - stepX * j;
				int y = slotY - stepY * j;
				
				int currentScore = 1;
				
				for(int k = 0; k < 4; k++)
				{
					int cell = getAt(x, y);
					if(cell == ((currentPlayer == 1) ? 2 : 1))
					{
						score += currentScore * 5;
					}
					else if (cell == currentPlayer && opponentRow != 4)
					{
						opponentRow++;
					}
					else if (opponentRow == 3)
					{
						score += currentScore * 10;
					}
					else if(cell != 0)
					{
						currentScore = 0;
						break;
					}
					
					x += stepX;
					y += stepY;
				}
				score += currentScore;
			}
		}
		return score;
	}
	private int getNextFreeSlot(int x)
	{
		for(int y = 0; y < row; y++)
		{
			if(getAt(x,y) < 1)
				return y;
		}
		return -1;
	}
	private int getAt(int x, int y)
	{
		if(x < 0 || y < 0 || x >= col || y >= row)
		{
			return -1;
		}
		else return (int)grid[x][y];
	}
	
	public boolean checkDraw()
	{
		boolean draw = true;
		for (int c = 0; c < col; ++c)
		{
			if (fillArray[c] < row)
			{
				draw = false;
				break;
			}
		}
		return draw;
	}
	
	public boolean checkWin()
	{
		int c = move[0], r = move[1];
		byte lastPlayer = (byte)((currentPlayer == 1) ? 2 : 1);		

		// Upwards victory
		if (r+1 < row && r+3 < row)
		{
			if (grid[c][r] == lastPlayer && grid[c][r+1] == lastPlayer && 
				grid[c][r+2] == lastPlayer && grid[c][r+3] == lastPlayer)
			{
				return true;
			}
		}
		if (r-1 >= 0 && r-3 >= 0)
		{
			// Downwards victory
			if (grid[c][r] == lastPlayer && grid[c][r-1] == lastPlayer && 
				grid[c][r-2] == lastPlayer && grid[c][r-3] == lastPlayer)
				{
					return true;
				}
		}
		if (c+1 < col && c+3 < col)
		{
			// Right-side victory
			if (grid[c][r] == lastPlayer && grid[c+1][r] == lastPlayer && 
				grid[c+2][r] == lastPlayer && grid[c+3][r] == lastPlayer)
				{
					return true;
				}
			
			if (r-1 >= 0 && r-3 >= 0)
			{
				//Right-down victory
				if (grid[c][r] == lastPlayer && grid[c+1][r-1] == lastPlayer && 
					grid[c+2][r-2] == lastPlayer && grid[c+3][r-3] == lastPlayer)
					{
						return true;
					}
			}
			
			if (r+1 < row && r+3 < row)
			{
				//Right-up victory
				if (grid[c][r] == lastPlayer && grid[c+1][r+1] == lastPlayer && 
					grid[c+2][r+2] == lastPlayer && grid[c+3][r+3] == lastPlayer)
					{
						return true;
					}
			}
		}
		if (c-1 >= 0 && c-3 >= 0)
		{
			// Left-side victory
			if (grid[c][r] == lastPlayer && grid[c-1][r] == lastPlayer && 
				grid[c-2][r] == lastPlayer && grid[c-3][r] == lastPlayer)
				{
					return true;
				}
			
			if (r-1 >= 0 && r-3 >= 0)
			{
				//Left-down victory
				if (grid[c][r] == lastPlayer && grid[c-1][r-1] == lastPlayer && 
					grid[c-2][r-2] == lastPlayer && grid[c-3][r-3] == lastPlayer)
					{
						return true;
					}
			}
			
			if (r+1 < row && r+3 < row)
			{
				//Left-up victory
				if (grid[c][r] == lastPlayer && grid[c-1][r+1] == lastPlayer && 
					grid[c-2][r+2] == lastPlayer && grid[c-3][r+3] == lastPlayer)
					{
						return true;
					}
			}
		}
		
		if (c - 1 >= 0 && c + 2 < col)
		{
			if (grid[c-1][r] == lastPlayer && grid[c][r] == lastPlayer && 
				grid[c+1][r] == lastPlayer && grid[c+2][r] == lastPlayer)
				{
					return true;
				}
			
			if (r - 1 >= 0 && r + 2 < row)
			{
				if (grid[c-1][r-1] == lastPlayer && grid[c][r] == lastPlayer && 
					grid[c+1][r+1] == lastPlayer && grid[c+2][r+2] == lastPlayer)
					{
						return true;
					}
			}
			
			if (r - 2 >= 0 && r + 1 < row)
			{
				if (grid[c-1][r+1] == lastPlayer && grid[c][r] == lastPlayer && 
					grid[c+1][r-1] == lastPlayer && grid[c+2][r-2] == lastPlayer)
					{
						return true;
					}
				
				if (grid[c-1][r+1] == lastPlayer && grid[c][r] == lastPlayer && 
					grid[c+1][r-1] == lastPlayer && grid[c+2][r-2] == lastPlayer)
					{
						return true;
					}
			}
			
		}
		

		if (c - 2 >= 0 && c + 1 < col)
		{
			if (grid[c-2][r] == lastPlayer && grid[c-1][r] == lastPlayer && 
					grid[c][r] == lastPlayer && grid[c+1][r] == lastPlayer)
					{
						return true;
					}
			
			if (r - 1 >= 0 && r + 2 < row)
			{
				if (grid[c-2][r-1] == lastPlayer && grid[c-1][r] == lastPlayer && 
					grid[c][r+1] == lastPlayer && grid[c+1][r+2] == lastPlayer)
					{
						return true;
					}
				if (grid[c-2][r+2] == lastPlayer && grid[c-1][r+1] == lastPlayer && 
					grid[c][r] == lastPlayer && grid[c+1][r-1] == lastPlayer)
					{
						return true;
					}
			}
			
			if (r - 2 >= 0 && r + 1 < row)
			{
				if (grid[c-2][r-2] == lastPlayer && grid[c-1][r-1] == lastPlayer && 
					grid[c][r] == lastPlayer && grid[c+1][r+1] == lastPlayer)
					{
						return true;
					}
				if (grid[c-2][r+1] == lastPlayer && grid[c-1][r] == lastPlayer && 
						grid[c][r-1] == lastPlayer && grid[c+1][r-2] == lastPlayer)
						{
							return true;
						}
			}
		}
		

		
		return false;
	}
	
	public void updateAdjacentNo()
	{
		int c = move[0], r = move[1];
		boolean hasAdjacent = false;
		if (r+1 < row)
		{
			if (grid[c][r+1] == currentPlayer)
			{
				hasAdjacent = true;
			}
		}
		if (r-1 >= 0)
		{
			// Downwards victory
			if (grid[c][r-1] == currentPlayer)
			{
				hasAdjacent = true;
			}
		}
		if (c+1 < col)
		{
			// Right-side victory
			if (grid[c+1][r] == currentPlayer)
				{
				hasAdjacent = true;
				}
			
			if (r-1 >= 0)
			{
				//Right-down victory
				if (grid[c+1][r-1] == currentPlayer)
					{
						hasAdjacent = true;
					}
			}
			
			if (r+1 < row)
			{
				//Right-up victory
				if (grid[c+1][r+1] == currentPlayer)
					{
					hasAdjacent = true;
					}
			}
		}
		if (c-1 >= 0)
		{
			// Left-side victory
			if (grid[c-1][r] == currentPlayer)
				{
					hasAdjacent = true;
				}
			
			if (r-1 >= 0)
			{
				//Left-down victory
				if (grid[c-1][r-1] == currentPlayer)
					{
						hasAdjacent = true;
					}
			}
			
			if (r+1 < row)
			{
				//Left-up victory
				if (grid[c-1][r+1] == currentPlayer)
					{
						hasAdjacent = true;
					}
			}
		}
		
		if (hasAdjacent)
		{
			if (currentPlayer == 1)
			{
				adjWhite++;
			}
			else
			{
				adjRed++;
			}
		}
	}
	
	public void print()
	{
		System.out.println("Current Player: " + currentPlayer);
		System.out.println("Terminal State: " + terminalTest());
		for (int y = row-1; y >= 0; --y)
		{
			for (int x = 0; x < col; ++x)
			{
				System.out.print("[ " + grid[x][y] + " ]");	
			}
			System.out.println();
		}
		System.out.println();
		System.out.println("White adjacent: " + adjWhite);
		System.out.println("Red adjacent: " + adjRed);
		
	}
	
}
